/**
 * 版权所有(C)，上海海鼎信息工程股份有限公司，2009，所有权利保留。
 * 
 * 项目名：	rumba-widget2
 * 文件名：	RDateBox.java
 * 模块说明：	
 * 修改历史：
 * Feb 10, 2009 - lxm - 创建。
 */
package com.hd123.rumba.gwt.widget2.client.form;

import java.util.Date;

import com.google.gwt.event.dom.client.BlurEvent;
import com.google.gwt.event.dom.client.BlurHandler;
import com.google.gwt.event.dom.client.KeyCodes;
import com.google.gwt.event.dom.client.KeyPressEvent;
import com.google.gwt.event.dom.client.KeyPressHandler;
import com.google.gwt.i18n.client.DateTimeFormat;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.Widget;
import com.hd123.rumba.gwt.util.client.RDateUtil;
import com.hd123.rumba.gwt.widget2.client.button.IsRButton;
import com.hd123.rumba.gwt.widget2.client.fielddef.DateFieldDef;
import com.hd123.rumba.gwt.widget2.client.fielddef.FieldDef;

/**
 * 日期输入框。
 * <p>
 * 
 * <b>控件元素结构</b><br>
 * div@super<br>
 * ..label@super<br>
 * ..div class=field@super<br>
 * ....input type=text class=input<br>
 * ....CalendarButton<br>
 * ....[? class=buttons]@super<br>
 * ....[div class=suffix]@super<br>
 * ....[? class=tip]@super<br>
 * ....[div class=note]@super
 * <p>
 * 
 * <b>CSS Style Rules</b> <ol type=disc>
 * <li>.rb-DateBox {}</li>
 * <li>.rb-DateBox-disabled {}</li>
 * <li>.rb-DateBox <i>Super</i> {}</li>
 * <li>.rb-DateBox .calendarButton {}</li>
 * <li>.rb-DateBox .calendarButton-hovering {}</li>
 * <li>.rb-DateBox .calendarButton-focused {}</li>
 * <li>.rb-DateBox .calendarButton-focused-hovering {}</li>
 * <li>.rb-DateBox .calendarButton-frozen {}</li>
 * <li>.rb-DateBox .calendarButton-frozen-focused {}</li> </ol>
 * 
 * @see RPopupCalendar
 * @author lxm
 * 
 */
public class RDateBox extends RTextBoxBase {

  private static final String STYLENAME_DEFAULT = "rb-DateBox";

  /**
   * 创建对象实例。
   */
  public RDateBox() {
    super(new RTextInput());
    ((TextBox) getInput()).setMaxLength(10);
    messages = new Messages();
    calButton = new CalendarButton();
    addButton(calButton);
    setStyleName(STYLENAME_DEFAULT);
    Handler handler = new Handler();
    addKeyPressHandler(handler);
    addBlurHandler(handler);
  }

  /**
   * @param fieldDef
   *          字段定义
   */
  public RDateBox(FieldDef fieldDef) {
    this();
    setFieldDef(fieldDef);
  }

  private CalendarButton calButton;
  private RPopupCalendar popup;
  private Date value;
  private Date minValue;
  private Date maxValue;
  private DateTimeFormat format = DateTimeFormat.getFormat("yyyy.M.d");
  private char seperator = '.';
  private Messages messages;

  /**
   * 取得当前取值。
   * 
   * @return
   */
  public Date getValue() {
    return value;
  }

  public void setValue(Date value) {
    this.value = value == null ? null : RDateUtil.datePart(value);
    if (this.value == null)
      setText("");
    else
      setText(format.format(this.value));
  }

  /**
   * 清除输入值
   * 
   */
  public void clearValue() {
    setValue(null);
  }

  /** 最小值。null表示不约束最小值。 */
  public Date getMinValue() {
    return minValue;
  }

  /** 最小值。null表示不约束最小值。 */
  public void setMinValue(Date minValue) {
    this.minValue = minValue == null ? null : RDateUtil.datePart(minValue);
  }

  /** 最大值。null表示不约束最大值。 */
  public Date getMaxValue() {
    return maxValue;
  }

  /** 最大值。null表示不约束最大值。 */
  public void setMaxValue(Date maxValue) {
    this.maxValue = maxValue == null ? null : RDateUtil.datePart(maxValue);
  }

  /** 日期格式。不允许为null，默认为"yyyy.M.d"。 */
  public DateTimeFormat getFormat() {
    return format;
  }

  /** 日期格式。不允许为null，默认为"yyyy.M.d"。 */
  public void setFormat(DateTimeFormat format) {
    this.format = format == null ? DateTimeFormat.getFormat("yyyy.M.d") : format;
    // 取得分隔符
    if (format.getPattern().indexOf('-') >= 0)
      seperator = '-';
    else
      seperator = '.';
  }

  /**
   * 取得提示信息对象。
   * 
   * @return
   */
  public Messages getMessages() {
    return messages;
  }

  public void setEnabled(boolean enabled) {
    super.setEnabled(enabled);
    refreshCalendarButton();
  }

  public void setReadOnly(boolean readonly) {
    super.setReadOnly(readonly);
    refreshCalendarButton();
  }

  public void setFieldDef(FieldDef fieldDef) {
    super.setFieldDef(fieldDef);

    if (fieldDef instanceof DateFieldDef) {
      setFormat(((DateFieldDef) fieldDef).getFormat());
    }
  }

  protected void onValidate() {
    super.onValidate();

    String inputText = getText();
    if ("".equals(inputText))
      return;

    Date newValue;
    try {
      newValue = RDateUtil.datePart(format.parse(inputText));
    } catch (Exception e) {
      getTip().addError(getTipPrefix() + messages.getInvalidText());
      return;
    }

    if (maxValue != null)
      if (newValue.after(maxValue))
        getTip().addError(getTipPrefix() + messages.getMaxText());
    if (minValue != null)
      if (newValue.before(minValue))
        getTip().addError(getTipPrefix() + messages.getMinText());
  }

  protected boolean beforeInputChange() {
    boolean result = super.beforeInputChange();
    if (result) {
      try {
        String s = getText();
        Date d = format.parse(s);
        value = RDateUtil.datePart(d);
      } catch (Exception e) {
        value = null;
      }
    } else {
      value = null;
    }
    return result;
  }

  private RPopupCalendar getPopup() {
    if (popup == null) {
      popup = new RPopupCalendar();
      popup.setCalendarListener(new Listener_popup());
    }
    return popup;
  }

  private void refreshCalendarButton() {
    calButton.setFrozen(!isEnabled() || isReadOnly());
  }

  /**
   * 提示信息
   * 
   * @author lxm
   * 
   */
  public class Messages {

    private String invalidText = "输入的不是合法的日期。";
    private String maxText;
    private String minText;

    /** 当输入的不是一个合法的日期的时候，显示的提示信息。默认为“输入的不是合法的日期。” */
    public String getInvalidText() {
      return invalidText;
    }

    /** 当输入的不是一个合法的日期的时候，显示的提示信息。默认为“输入的不是合法的日期。” */
    public void setInvalidText(String invalidText) {
      this.invalidText = invalidText;
    }

    /** 当输入大于最大值时显示的信息，默认为“输入日期大于最大日期{0}。” */
    public String getMaxText() {
      if (maxText != null)
        return maxText;
      if (RDateBox.this.maxValue == null)
        return "";
      return "输入日期大于最大日期\"" + RDateBox.this.format.format(RDateBox.this.maxValue) + "\"。";
    }

    /** 当输入大于最大值时显示的信息，默认为“输入日期大于最大日期{0}。” */
    public void setMaxText(String maxText) {
      this.maxText = maxText;
    }

    /** 当输入小于最大值时显示的信息，默认为“输入日期小于最小日期{0}。” */
    public String getMinText() {
      if (minText != null)
        return minText;
      return "输入日期小于最小日期\"" + RDateBox.this.format.format(RDateBox.this.minValue) + "\"。";
    }

    /** 当输入小于最大值时显示的信息，默认为“输入日期小于最小日期{0}。” */
    public void setMinText(String minText) {
      this.minText = minText;
    }
  }

  private class CalendarButton extends Widget implements IsRButton {

    public CalendarButton() {
      super();
      setElement(DOM.createDiv());
      setStyleName("calendarButton");
      sinkEvents(Event.MOUSEEVENTS | Event.ONCLICK);
    }

    private boolean hovering = false;
    private boolean focused = false;
    private boolean frozen = false;

    public boolean isShowIcon() {
      return true;
    }

    public void setShowIcon(boolean showIcon) {
      // Do Nothing
    }

    public boolean isShowText() {
      return false;
    }

    public void setShowText(boolean showText) {
      // Do Nothing
    }

    /** 是否获得焦点。 */
    public boolean isFocused() {
      return focused;
    }

    /** 是否获得焦点。 */
    public void setFocused(boolean focused) {
      this.focused = focused;
      refreshStyle();
    }

    /** 是否被冻结。有两种情况导致冻结：一种是输入框disabled，另一种是输入框readonly。 */
    public boolean isFrozen() {
      return frozen;
    }

    /** 是否被冻结。有两种情况导致冻结：一种是输入框disabled，另一种是输入框readonly。 */
    public void setFrozen(boolean frozen) {
      this.frozen = frozen;
      if (this.frozen)
        DOM.setElementAttribute(getElement(), "disabled", "true");
      else
        DOM.removeElementAttribute(getElement(), "disabled");
      refreshStyle();
    }

    public void onBrowserEvent(Event event) {
      super.onBrowserEvent(event);

      if (isFrozen())
        return;

      switch (DOM.eventGetType(event)) {
      case Event.ONCLICK:
        doClick();
        break;
      case Event.ONMOUSEOVER:
        setHovering(true);
        break;
      case Event.ONMOUSEOUT:
        setHovering(false);
        break;
      }
    }

    public void doClick() {
      if (value != null)
        getPopup().setSelectedDate(value);
      else
        getPopup().setSelectedDate(new Date());

      getPopup().setPopupPositionAndShow(new RPopupCalendar.PositionCallback() {
        public void setPosition(int offsetWidth, int offsetHeight) {
          // 计算Left
          int left = CalendarButton.this.getAbsoluteLeft() + CalendarButton.this.getOffsetWidth()
              - offsetWidth;
          if (left < 0)
            left = 0;

          // 计算Top
          int top = getInput().getAbsoluteTop() + getInput().getOffsetHeight();
          if (top + offsetHeight > Window.getScrollTop() + Window.getClientHeight()) {
            int top2 = getInput().getAbsoluteTop() - offsetHeight;
            if (top2 > Window.getScrollTop())
              top = top2;
          }

          getPopup().setPopupPosition(left, top);
        }
      });
    }

    private void setHovering(boolean hovering) {
      this.hovering = hovering;
      refreshStyle();
    }

    private void refreshStyle() {
      StringBuffer sb = new StringBuffer();
      sb.append(getStylePrimaryName());
      sb.append(" ");
      sb.append(getStylePrimaryName());
      if (frozen)
        sb.append("-frozen");
      if (focused)
        sb.append("-focused");
      if (!frozen && hovering)
        sb.append("-hovering");
      setStyleName(sb.toString());
    }
  }

  private class Listener_popup implements RPopupCalendarListener {
    public void onSelected(RPopupCalendar sender, Date selectedDate) {
      getTip().clear();
      setValue(selectedDate);
      getInput().setFocus(true);
      getInput().selectAll();
      fireChange();
    }
  }

  /**
   * 自身的事件处理
   * 
   * @author LiQi
   * 
   */
  private class Handler implements KeyPressHandler, BlurHandler {

    private static final String DIGIT_CHARACTER = "1234567890";

    public void onKeyPress(KeyPressEvent event) {
      int charCode = event.getCharCode();
      if (charCode == KeyCodes.KEY_LEFT || charCode == KeyCodes.KEY_RIGHT
          || charCode == KeyCodes.KEY_UP || charCode == KeyCodes.KEY_DOWN
          || charCode == KeyCodes.KEY_ENTER || charCode == KeyCodes.KEY_TAB
          || charCode == KeyCodes.KEY_BACKSPACE)
        return;

      // 如果是DIGIT_CHARACTER和seperator以外的键，直接取消。
      if (DIGIT_CHARACTER.indexOf(charCode) < 0 && charCode != seperator) {
        event.preventDefault();
        return;
      }
    }

    public void onBlur(BlurEvent event) {
      if (!isValid())
        return;

      // 格式化显示
      if (value != null)
        setText(format.format(value));
    }
  }
}
